const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 1. 传进来执行器立即执行  向执行器传入 resolve reject
// 2. 定义promise的状态
// 3. resolve reject  用来改变状态  注意如果状态不是PENDING 就不做任何改变
// 4. then 判断状态 调用分别的方法 成功就调用成功
// 5. 保存成功失败的值
// 6. 异步情况 将成功回调 和失败回调存储起来
// 7. 执行resolve reject的时候判断成功或者失败回调是否存在 在的话 就调用
// 8. then多次调用 所以要把回调函数存进数组里
// 9. then的链式调用  
// 10. then返回一个新的promise  原先的逻辑搬到 新的promise 执行器里面去执行  接收then的返回值
//     也就是回调函数的返回值  传入新的promise的 resolve 
// 11 判断then返回的值x  是普通对象还是promise对象   普通值直接resolve 
//     如果是promise对象的话 查看promise对象返回的结果
//     在根据promise对象返回的结果  决定调用resolve 还是 reject
// 12 写个resolvePromise 对这个值进行统一的判断
// 13 判断promise的循环调用 
// 14 所以要把promise2 跟 返回的x 作比较 但是这个时候 还没办法获取到promise2  这个时候要做异步的处理获取到promise2 
// 15 捕获执行器的错误
// 16 捕获then 回调错误
// 17 失败回调做个处理  
// 18 异步情况 处理  要在外面包一层函数 做异步处理
// 19 then 成功失败 参数可选 默认值设置
// 20 all 静态方法 
// 21 resolve 静态方法
// 22 finally 



class MyPromise{
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  status = ''
  value = undefined
  reason = undefined
  successCallback = []
  failCallback = []
  resolve = value => {
    if(this.status!==PENDING) return
    this.status = FULFILLED
    this.value = value
    while(this.successCallback.length) {
      this.successCallback.shift()()
    }
  }
  reject = reason => {
    if(this.status!==PENDING) return
    this.status = REJECTED
    this.reason = reason
    while(this.failCallback.length) {
      this.failCallback.shift()()
    }
  }
  then(successCallback, failCallback) {
    successCallback = successCallback || (value => value);
    failCallback = successCallback || (reason => {throw reason})
    let promise2 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) {
        setTimeout(()=>{
          try {
            let x = successCallback(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      }else if(this.status === REJECTED){
        setTimeout(()=>{
          try {
            let x = failCallback(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        },0)
      }else{
        this.successCallback.push(() => {
          setTimeout(()=>{
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          },0)
        })
        this.failCallback.push(()=>{
          setTimeout(()=>{
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          },0)
        })
      }

    })
    
    return promise2
  }

  finally(callback) {
    return this.then(value=>{
      return MyPromise.resolve(callback()).then(()=>value)
    }, ()=>{
      return MyPromise.resolve(callback()).then(()=>{throw reason})
    })
  }

  catch(failCallback) {
    return this.then(undefined, failCallback)
  }

  static all(array) {
    let res = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        res[key] = value
        index++
        if(index === array.length) {
          resolve(res)
        }
      }
      for(let i = 0; i<array.length; i++) {
        let current = array[i]
        if(current instanceof MyPromise) {
          current.then((value)=>addData(i, value), (reason)=>reject(reason))
        }else{
          addData(i, current)
        }
      }
    })
  }

  static resolve(value) {
    if(value instanceof MyPromise) return value
    return new Promise(resolve => resolve(value))
  }




}



function resolvePromise(promise2, x, resolve, reject) {
  // 这边如果要兼容其他 promise的话  就不能这样写 
  if(promise2 === x) {
    return reject(new TypeError('循环错误'))
  }
  if(x instanceof MyPromise) {
    x.then(resolve, reject)
  }else{
    resolve(x)
  }
}